package com.ruoyi.framework.tenant;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.tenant.TenantFrameworkService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.tenant.TenantContextHolder;
import com.ruoyi.framework.web.exception.GlobalExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;


public class TenantSecurityWebFilter extends OncePerRequestFilter {

    private static Logger log = LoggerFactory.getLogger(TenantSecurityWebFilter.class);

    private final TenantProperties tenantProperties;

    private final AntPathMatcher pathMatcher;

    private final GlobalExceptionHandler globalExceptionHandler;

    private final TenantFrameworkService tenantFrameworkService;

    public TenantSecurityWebFilter(TenantProperties tenantProperties,
                                   GlobalExceptionHandler globalExceptionHandler,
                                   TenantFrameworkService tenantFrameworkService) {
        this.tenantProperties = tenantProperties;
        this.pathMatcher = new AntPathMatcher();
        this.globalExceptionHandler = globalExceptionHandler;
        this.tenantFrameworkService = tenantFrameworkService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (tenantProperties.getEnable()) {
            Long tenantId = TenantContextHolder.getTenantId();
            // 1. 登陆的用户，校验是否有权限访问该租户，避免越权问题。
            LoginUser user = SecurityUtils.getLoginUser();
            if (user != null) {
                // 如果获取不到租户编号，则尝试使用登陆用户的租户编号
                if (tenantId == null) {
                    tenantId = user.getTenantId();
                    TenantContextHolder.setTenantId(tenantId);
                    // 如果传递了租户编号，则进行比对租户编号，避免越权问题
                } else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {
                    log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
                            user.getTenantId(), user.getUserId(), user.getUsername(),
                            TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());
                    write(response, AjaxResult.error("您无权访问该租户的数据"));
                    return;
                }
            }

            if (!isIgnoreUrl(request)) {
                // 2. 如果请求未带租户的编号，不允许访问。
                if (tenantId == null) {
                    log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod());
                    write(response, AjaxResult.error("租户的请求未传递，请进行排查"));
                    return;
                }
                // 3. 校验租户是合法，例如说被禁用、到期
                try {
                    tenantFrameworkService.validTenant(tenantId);
                } catch (Throwable ex) {
                    write(response, AjaxResult.error(ex.getMessage()));
                    return;
                }
            } else { // 如果是允许忽略租户的 URL，若未传递租户编号，则默认忽略租户编号，避免报错
                if (tenantId == null) {
                    TenantContextHolder.setIgnore(true);
                }
            }
        }
        // 继续过滤
        filterChain.doFilter(request, response);
    }

    private void write( HttpServletResponse response,AjaxResult content){
        ServletUtil.write(response, JSON.toJSONString(content), MediaType.APPLICATION_JSON_UTF8_VALUE);
    }
    private boolean isIgnoreUrl(HttpServletRequest request) {
        // 快速匹配，保证性能
        if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) {
            return true;
        }
        // 逐个 Ant 路径匹配
        for (String url : tenantProperties.getIgnoreUrls()) {
            if (pathMatcher.match(url, request.getRequestURI())) {
                return true;
            }
        }
        return false;
    }
}
